#ifndef _basic_bitstream_hpp_
#define _basic_bitstream_hpp_

/** \file basic_bitstream.hpp
 *  \author Adrian Schweizer
 *  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $So 19 Aug 01:36:08 pm CEST 2007 schwadri@SchwadriComp.local$
 */

#include <iosfwd>
#include <boost/optional.hpp>

namespace core {

    /** \brief A bitstream interface
     *
     *  \note This class was inspired by the Bitstream class from RakNet. It has been stripped down of any
     *  features that don't really belong into such a class and has been made more generic. Additionally
     *  bitstream archive classes are provided for usage with the boost::archive framework (see bitstream_archive.hpp).
     *  Methods are provided for std::stream-like manipulation of the read/write pointer. But still
     *  the the name is a bit misleading as bitstream does no formatting whatsoever. so maybe bitbuffer
     *  would be a more adequate name.
     *
     *  \par TODO
     *  - create some usage examples
     *
     *  \par Automatic Endian Swapping
     *  The methods for reading and writing integral types (namely read, read_aligned, write, write_aligned) do automatic endian swapping on
     *  little endian platforms as the target platform is big endian.
     *  If you are on a little endian platform and want to disable endian swapping define CORE_BITSTREAM_NATIVE_END.
     */
    template<class Impl>
        class basic_bitstream
        {
            typedef Impl                            impl_type;
            typedef basic_bitstream<Impl>           my_type;
        public:
            typedef typename impl_type::pos_type    pos_type;
            typedef typename impl_type::size_type   size_type;
            typedef typename impl_type::block_type  block_type;

            /** \brief Positioning modes for \sa seekp and \sa seekg */
            enum    rel_type
            {
                cur=0x0,     ///< Offset from current position
                beg=0x01,    ///< Offset from buffer beginning
                end=0x02     ///< Offset from buffer end
            };
            /** Default Constructor*/
            basic_bitstream();

            /** Create the bitstream, with some number of bits to immediately allocate.
             *  There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE.
             *  In that case all it does is save you one or more realloc calls.
             *  \param[in] size the number of bits to pre-allocate. (This value is rounded up to the next byte boundary)
             */
            basic_bitstream(size_type bit_count);

            /** Initialize the bitstream, immediately setting the data it contains to a predefined pointer.
             *  Set \a copy_data to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data.
             *  You shouldn't call Write functions with \a copy_data as false, as this will write to unallocated memory
             *  99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows:
             *  \code
             *  network::bitstream bs(packet->data, packet->length, false);
             *  \endcode
             *  \param[in] data An array of bytes.
             *  \param[in] bit_count Size of the \a _data.
             *  \param[in] copy_data true or false to make a copy of \a data or not.
             */
            basic_bitstream(block_type* data, size_type bit_count, bool copy_data=false);

            /** \brief copy constructor. behaviour is implementation dependent.
             *
             *  \param[in] other the bitstream for construction
             *  \param[in] copy_data whether to copy tha data or not. This defaults to \a false.
             *  \warning If this is false, \a other has to exit until this copy is destroyed, because \a other decides, when
             *  its memory has to be released
             */
            basic_bitstream(my_type& other, bool copy_data=false);

            /** \brief constructing using a bitstream with different implementation*/
            template<class OImpl>
                basic_bitstream(const basic_bitstream<OImpl>&   other, bool copy_data=false);

            /**
             * \warning DON'T USE! NOT IMPLEMENTED
             */
            template<class BlockInputIterator>
                basic_bitstream(BlockInputIterator begin, BlockInputIterator end);

            /** \brief Construct bitstream using data from a Container, that provides a forward iterator
             *  \param[in] data Container providing the iterator functions \a .begin() and \a .end(), which
             *  return a forward iterator*/
            template<class Container>
                basic_bitstream(Container&  data, bool copy_data=false);

            /** Destructor*/
            ~basic_bitstream();

            /** \brief makes sure that the underlying buffer has at least enough space for \a bit_size bits. Any data previously written
             *  to the bitstream will be lost.
             */
            void reserve(size_type bit_size);

            /** Resets the bitstream for reuse.*/
            void reset();

            /* \name write methods*/ //@{

            /** Write any integral type to a bitstream.  Define  CORE_BITSTREAM_NATIVE_END to disable endian swapping.
             *  \param[in] var The value to write
             */
            template <class T>
                void write(const T& var);

            /** Write any byte aligned integral type to a bitstream.  Define CORE_BITSTREAM_NATIVE_END to disable endian swapping.
             *  \param[in] var The value to write
             */
            template <class T>
                void write_aligned(const T& var);

            /** \brief write elements of the iterator range [begin,end-1] to the stream
             *
             *  This Operation is byte_aligned
             *  \param[in] begin Forwarditerator pointing to the first element of the range
             *  \param[in] end Points one after the last element of the copy range
             */
            template<class ForwardIterator>
                void write_aligned(ForwardIterator begin, ForwardIterator end);

            /** Write an array or casted stream or raw data.  This does NOT do endian swapping.
             *  \param[in] input a byte buffer
             *  \param[in] len the size of \a input in bytes
             */
            void write(const char* input, size_type len);

            /** Write count bits from the input source Right aligned
             *  data means in the case of a partial byte, the bits are aligned
             *  from the right (bit 0) rather than the left (as in the normal
             *  internal representation) You would set this to true when
             *  writing user data, and false when copying bitstream data, such
             *  as writing one bitstream to another
             *  \param[in] input The data
             *  \param[in] bit_count The number of bits to write
             *  \param[in] rightAlignedBits if true data will be right aligned
             */
            void write_bits( const unsigned char* input, size_type bit_count, bool right_aligned = true );

            /** Align the bitstream to the byte boundary and then write the
             *  specified number of bits.  This is faster than write_bits but
             *  wastes the bits to do the alignment and requires you to call
             *  read_aligned_bytes at the corresponding read position.
             *  \param[in] input The data
             *  \param[in] numberOfBytesToWrite The size of data.
             */
            void write_aligned_bytes( const unsigned char *input, size_type byte_count );

            //@}

            /** Read any integral type from a bitstream.  Define CORE_BITSTREAM_NATIVE_END if you want to disable endian swapping.
             *  \param[in] var The value to read
             */
            template <class T>
                bool read(T &var);

            /** Read any byte aligned integral type from a bitstream.  Define CORE_BITSTREAM_NATIVE_END if you want to disable endian swapping.
             *  \param[in] var The value to read
             */
            template <class T>
                bool read_aligned(T &var);

            /** \brief read a integral value without advancing the read_offset
             */
            template<class T>
                boost::optional<T>   peek() const;

            /** \brief same as peek but aligned
             */
            template<class T>
                boost::optional<T>   peek_aligned() const;

            /** \brief read elements from the stream into the iterator range [begin,end-1]
             *
             *  This Operation is byte_aligned
             *  \param[in] begin Forwarditerator pointing to the first element of the range
             *  \param[in] end Points one after the last element of the copy range
             *  \return true on success false if there where not enough bytes
             */
            template<class ForwardIterator>
                bool read_aligned(ForwardIterator begin, ForwardIterator end);


            /** Read an array or casted stream of byte. The array
             *  is raw data. There is no automatic endian conversion with this function
             *  \param[in] output The result byte array. It should be larger than @em byte_count.
             *  \param[in] byte_count The number of byte to read
             *  \return true on success false if there is some missing bytes.
             */
            bool read(char* output, size_type byte_count);

            /** \brief Absolute positioning of the read-pointer.
             *
             *  Sets the read pointer to the specified position \a pos, measured in bits from the beginning of the underlying data buffer.
             *  \param[in] pos The new absolute position of the read pointer specified in bits
             */
            void seekg(pos_type pos);

            /** \brief Relative positioning of the read-pointer
             *
             *
             *  \param[in] pos
             *  \param[in] rel
             */
            void seekg(pos_type pos,rel_type rel);

            /** \brief Retreive current read pointer position.
             *
             *  Returns the current read pointer position in bits, or
             *  respectively the number of bits into the stream that we have read
             */
            pos_type    tellg() const;

            /** \brief Absolute positioning of the write pointer
             *
             *  Move the write pointer to a position relative to the start of the array.
             *  \param[in] pos The position in bits
             */
            void seekp(pos_type pos);

            /** \brief Relative positioning of the write pointer
             *
             *  \param[in] pos
             *  \param[in] rel
             */
            void seekp(pos_type pos,rel_type rel);

            /** \brief Get current write pointer position in bits
             */
            pos_type    tellp() const;

            /** \brief ignores one bit. This is the same as seekg(1,cur) or
             *  ignore<bool>() */
            void        ignore();

            /** \brief ignores \a bit_count bits
             *
             *  Ignore data we don't intend to read. This is the same as seekg(count,cur).
             *  \param[in] count The number of bits to ignore
             */
            void        ignore(size_type count);

            /** \brief ignores \a sizeof(T) bits
             *
             *  Usage:
             *  \code
             *  bitstream bs;
             *  ...
             *  bs.ignore<int>();
             *  bs.ignore<double>();
             *  \endcode
             *  Alternatively you can use \a ignore(sizeof(T))
             */
            template<class T>
                void    ignore()
                {
                    ignore(sizeof(T)<<3);
                }

            /** \brief Returns the size in bits of the stream*/
            int size() const;

            /** \brief Returns the length in bytes of the stream*/
            int size_bytes() const;

            /** Makes a copy of the internal data for you \a data will point to
             *  the stream. Returns the length in bits of the stream. Partial
             *  bytes are left aligned
             *  \param[out] data The allocated copy of GetData()
             */
            int copy_data( unsigned char*& data ) const;

            /** Set the stream to some initial data.
             *  \internal
             *  Partial bytes are left aligned
             *  \param[in] input The data
             *  \param[in] bit_count the number of bits set in the data buffer
             */
            void set_data( const unsigned char* input, const size_type bit_count );

            /** Gets the data that bitstream is writing to / reading from
             *  \internal
             *  Partial bytes are left aligned.
             *  \return A pointer to the internal state
             */
            unsigned char* get_data() const;

            /** Read bits, starting at the next aligned bits. Note that the
             *  modulus 8 starting offset of the sequence must be the same as
             *  was used with WriteBits. This will be a problem with packet
             *  coalescence unless you byte align the coalesced packets.
             *  \param[in] output The byte array larger than @em len
             *  \param[in] len The number of bytes to read from the internal state
             *  \return true if there is enough bytes.
             */
            bool read_aligned_bytes( unsigned char *output, size_type byte_count );

            /** bytewise access to data*/
            unsigned char&  operator[](size_type pos);
            /** bytewise access to data*/
            unsigned char   operator[](size_type pos) const;

            /** Align the next write to a byte boundary.  This can
             *  be used to 'waste' bits to byte align for efficiency reasons It
             *  can also be used to force coalesced bitstreams to start on byte
             *  boundaries so so WriteAlignedBits and ReadAlignedBits both
             *  calculate the same offset when aligning.
             */
            void alignp();

            /** Align the next read to a byte boundary.  This can
             *  be used to 'waste' bits to byte align for efficiency reasons It
             *  can also be used to force coalesced bitstreams to start on byte
             *  boundaries so so WriteAlignedBits and ReadAlignedBits both
             *  calculate the same offset when aligning.
             */
            void aligng();

            /** Read \a count bits to the output source
             *  alignBitsToRight should be set to true to convert internal
             *  bitstream data to userdata. It should be false if you used
             *  write_bits with right_aligned false
             *  \param[in] output The resulting bits array
             *  \param[in] count The number of bits to read
             *  \param[in] right_aligned if true bits will be right aligned.
             *  \return true if there is enough bits to read
             */
            bool read_bits( unsigned char *output, size_type count, bool right_aligned = true );

            /** Write a 0*/
            void write0();

            /** Write a 1*/
            void write1();

            /** Reads 1 bit and returns true if that bit is 1 and false if it is 0*/
            bool readbit();

            /** Use this if you pass a pointer copy to the constructor
             *  (copyData==false) and want to overallocate to prevent
             *  reallocation
             */
            void set_number_of_bits_allocated( size_type bit_count );

            /** returns true if there are bits left to read
             */
            operator bool() const;

        private:
            impl_type   m_impl;
        };

    /** Prints a humanreadable binary representation to a stream. For debugging purposes
     *  FIXME: implement this!
     */
    template <class I, class _CharT, class _Traits>
        std::basic_ostream<_CharT,_Traits>& operator    <<(std::basic_ostream<_CharT,_Traits>& out,const basic_bitstream<I>& bs)
        {
            return out;
        }


    template<class I, class T>
        basic_bitstream<I>&  operator << (basic_bitstream<I>& bout,const T& val)
        {
            bout.write(val);
            return bout;
        }

    template<class I, class T>
        basic_bitstream<I>&  operator >> (basic_bitstream<I>& bin,T& val)
        {
            bin.read(val);
            return bin;
        }


} // namespace core

#include "detail/basic_bitstream_impl.hpp"
#endif // _basic_bitstream_hpp_
